package com.afkt.project.ui.widget.render.shapechange;



import com.afkt.project.util.LogUtils;
import com.dev.utils.app.SizeUtils;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.render.PathMeasure;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;


public class CoolWaitLoadingRenderer extends Component {

    private static String TAG = CoolWaitLoadingRenderer.class.getSimpleName();

    private final float DEFAULT_WIDTH        = 200.0f;
    private final float DEFAULT_HEIGHT       = 150.0f;
    private final float DEFAULT_STROKE_WIDTH = 8.0f;
    private final float WAIT_CIRCLE_RADIUS   = 50.0f;

    private static final float WAIT_TRIM_DURATION_OFFSET = 0.5f;
    private static final float END_TRIM_DURATION_OFFSET  = 1.0f;

    private final long ANIMATION_DURATION = 2222;

    private final Paint mPaint = new Paint();

    private final Path mWaitPath              = new Path();
    private final Path        mCurrentTopWaitPath    = new Path();
    private final Path        mCurrentMiddleWaitPath = new Path();
    private final Path        mCurrentBottomWaitPath = new Path();


    private  PathMeasure mWaitPathMeasure;

    private final RectFloat mCurrentBounds = new RectFloat();

    private float mStrokeWidth;
    private float mWaitCircleRadius;
    private float mOriginEndDistance;
    private float mOriginStartDistance;
    private float mWaitPathLength;

    private int mTopColor;
    private int mMiddleColor;
    private int mBottomColor;


    public CoolWaitLoadingRenderer(Context context) {
        super(context);
        init();
    }

    public CoolWaitLoadingRenderer(Context context, AttrSet attrSet) {
        super(context, attrSet);
        init();
    }

    public CoolWaitLoadingRenderer(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        init();
    }

    public CoolWaitLoadingRenderer(Context context, AttrSet attrSet, int resId) {
        super(context, attrSet, resId);
        init();
    }

    protected long mDuration;

    protected float mWidth;
    protected float mHeight;

    private AnimatorValue animatorValue;


    private void init() {
        ShapeElement shapeElement = new ShapeElement();
        shapeElement.setRgbColor(RgbColor.fromArgbInt(Color.getIntColor("#3F51B5")));
        setBackground(shapeElement);
        LogUtils.error(TAG, "init");
        mWidth = SizeUtils.dipConvertPx(DEFAULT_WIDTH);
        mHeight = SizeUtils.dipConvertPx(DEFAULT_HEIGHT);
        mStrokeWidth = SizeUtils.dipConvertPx(DEFAULT_STROKE_WIDTH);
        mWaitCircleRadius = SizeUtils.dipConvertPx(WAIT_CIRCLE_RADIUS);
        mTopColor = Color.WHITE.getValue();
        mMiddleColor = Color.getIntColor("#FFF3C742");
        mBottomColor = Color.getIntColor("#FF89CC59");
        mDuration = ANIMATION_DURATION;
        setupPaint();
        animatorValue = new AnimatorValue();
        animatorValue.setLoopedCount(AnimatorValue.INFINITE);
        animatorValue.setDuration(mDuration);
        animatorValue.setValueUpdateListener(mAnimatorUpdateListener);
        addDrawTask(drawTask);
        setLayoutRefreshedListener(new LayoutRefreshedListener() {
            @Override
            public void onRefreshed(Component component) {
                animatorValue.stop();
                animatorValue.start();
            }
        });
    }

    public void stop() {
        animatorValue.stop();
    }

    private void setupPaint() {
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(mStrokeWidth);
        mPaint.setStyle(Paint.Style.STROKE_STYLE);
        mPaint.setStrokeJoin(Paint.Join.ROUND_JOIN);
        mPaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
    }

    private DrawTask drawTask = new DrawTask() {
        @Override
        public void onDraw(Component component, Canvas canvas) {
            int   saveCount = canvas.save();
            RectFloat arcBounds = new RectFloat();
            copy(arcBounds, new Rect(getLeft() + getWidth()/4 , getTop() + getHeight()/2,
                    getRight() - getWidth() / 4 , getBottom() -getHeight() / 2 + 50));
            mPaint.setColor(new Color(mBottomColor));
            canvas.drawPath(mCurrentBottomWaitPath, mPaint);

            mPaint.setColor(new Color(mMiddleColor));
            canvas.drawPath(mCurrentMiddleWaitPath, mPaint);

            mPaint.setColor(new Color(mTopColor));
            canvas.drawPath(mCurrentTopWaitPath, mPaint);
            canvas.restoreToCount(saveCount);
        }
    };

    private Path createWaitPath(RectFloat bounds) {
        Path path = new Path();
        //create circle
        path.moveTo(bounds.getCenter().getPointX() + mWaitCircleRadius, bounds.getCenter().getPointY());

        //create w
        path.cubicTo(new Point(bounds.getCenter().getPointX() + mWaitCircleRadius, bounds.getCenter().getPointY() - mWaitCircleRadius * 0.5f),
                new Point(bounds.getCenter().getPointX() + mWaitCircleRadius * 0.3f, bounds.getCenter().getPointY() - mWaitCircleRadius),
                new Point(bounds.getCenter().getPointX() - mWaitCircleRadius * 0.35f, bounds.getCenter().getPointY() + mWaitCircleRadius * 0.5f));
        path.quadTo(bounds.getCenter().getPointX() + mWaitCircleRadius, bounds.getCenter().getPointY() - mWaitCircleRadius,
                bounds.getCenter().getPointX() + mWaitCircleRadius * 0.05f, bounds.getCenter().getPointY() + mWaitCircleRadius * 0.5f);
        path.lineTo(bounds.getCenter().getPointX() + mWaitCircleRadius * 0.75f, bounds.getCenter().getPointY() - mWaitCircleRadius * 0.2f);

        path.cubicTo(new Point(bounds.getCenter().getPointX(), bounds.getCenter().getPointY() + mWaitCircleRadius * 1f),
                new Point(bounds.getCenter().getPointX() + mWaitCircleRadius, bounds.getCenter().getPointX() + mWaitCircleRadius * 0.4f),
                new Point(bounds.getCenter().getPointX() + mWaitCircleRadius, bounds.getCenter().getPointX()));

        //create arc
        path.arcTo(new RectFloat(bounds.getCenter().getPointX() - mWaitCircleRadius, bounds.getCenter().getPointX() - mWaitCircleRadius,
                bounds.getCenter().getPointX() + mWaitCircleRadius, bounds.getCenter().getPointX() + mWaitCircleRadius), 0, -359);
        path.arcTo(new RectFloat(bounds.getCenter().getPointX() - mWaitCircleRadius, bounds.getCenter().getPointX() - mWaitCircleRadius,
                bounds.getCenter().getPointX() + mWaitCircleRadius, bounds.getCenter().getPointX() + mWaitCircleRadius), 1, -359);
        path.arcTo(new RectFloat(bounds.getCenter().getPointX() - mWaitCircleRadius, bounds.getCenter().getPointX() - mWaitCircleRadius,
                bounds.getCenter().getPointX() + mWaitCircleRadius, bounds.getCenter().getPointX() + mWaitCircleRadius), 2, -2);
        //create w
        path.cubicTo(new Point(bounds.getCenter().getPointX() + mWaitCircleRadius, bounds.getCenter().getPointX() - mWaitCircleRadius * 0.5f),
                new Point(bounds.getCenter().getPointX() + mWaitCircleRadius * 0.3f, bounds.getCenter().getPointX() - mWaitCircleRadius),
                new Point(bounds.getCenter().getPointX() - mWaitCircleRadius * 0.35f, bounds.getCenter().getPointX() + mWaitCircleRadius * 0.5f));
        path.quadTo(bounds.getCenter().getPointX() + mWaitCircleRadius, bounds.getCenter().getPointX() - mWaitCircleRadius,
                bounds.getCenter().getPointX() + mWaitCircleRadius * 0.05f, bounds.getCenter().getPointX() + mWaitCircleRadius * 0.5f);
        path.lineTo(bounds.getCenter().getPointX() + mWaitCircleRadius * 0.75f, bounds.getCenter().getPointX() - mWaitCircleRadius * 0.2f);

        path.cubicTo(new Point(bounds.getCenter().getPointX(), bounds.getCenter().getPointX() + mWaitCircleRadius * 1f),
                new Point(bounds.getCenter().getPointX() + mWaitCircleRadius, bounds.getCenter().getPointX() + mWaitCircleRadius * 0.4f),
                new Point(bounds.getCenter().getPointX() + mWaitCircleRadius, bounds.getCenter().getPointX()));

        return path;
    }

    private void copy(RectFloat src, Rect des) {
        src.left = des.left;
        src.right = des.right;
        src.bottom = des.bottom;
        src.top = des.top;
    }

    private AnimatorValue.ValueUpdateListener mAnimatorUpdateListener = new AnimatorValue.ValueUpdateListener() {
        @Override
        public void onUpdate(AnimatorValue animatorValue, float renderProgress) {
          //  LogUtils.error(TAG, "mCurrentBounds = " + mCurrentBounds.isEmpty());

      /*      if (mCurrentBounds.isEmpty()) {
                return;
            }*/

            LogUtils.error(TAG, "renderProgress = " + renderProgress);
            if (mWaitPath.isEmpty()) {
                mWaitPath.set(createWaitPath(mCurrentBounds));
                mWaitPathMeasure = new PathMeasure(mWaitPath, false);
                mWaitPathLength = mWaitPathMeasure.getLength();

                mOriginEndDistance = mWaitPathLength * 0.255f;
                mOriginStartDistance = mWaitPathLength * 0.045f;
            }

            mCurrentTopWaitPath.reset();
            mCurrentMiddleWaitPath.reset();
            mCurrentBottomWaitPath.reset();

            //draw the first half : top
            if (renderProgress <= WAIT_TRIM_DURATION_OFFSET) {
                float topTrimProgress  =renderProgress;
                float topEndDistance   = mOriginEndDistance + mWaitPathLength * 0.3f * topTrimProgress;
                float topStartDistance = mOriginStartDistance + mWaitPathLength * 0.48f * topTrimProgress;
                LogUtils.error(TAG, "topStartDistance = " + topStartDistance + " topEndDistance = " + topEndDistance + "length = " + mWaitPathMeasure.getLength());
                mWaitPathMeasure.getSegment(topStartDistance, topEndDistance, mCurrentTopWaitPath, true);
            }

            //draw the first half : middle
            if (renderProgress > 0.02f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= WAIT_TRIM_DURATION_OFFSET * 0.75f) {
                float middleStartTrimProgress = (renderProgress - 0.02f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.73f);
                float middleEndTrimProgress   = (renderProgress - 0.02f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.73f);

                float middleEndDistance   = mOriginStartDistance + mWaitPathLength * 0.42f * middleEndTrimProgress;
                float middleStartDistance = mOriginStartDistance + mWaitPathLength * 0.42f * middleStartTrimProgress;
                LogUtils.error(TAG, "middleStartDistance = " + middleStartDistance + " middleEndDistance = " + middleEndDistance + "length = " + mWaitPathMeasure.getLength());
                mWaitPathMeasure.getSegment(middleStartDistance, middleEndDistance, mCurrentMiddleWaitPath, true);
            }

            //draw the first half : bottom
            if (renderProgress > 0.04f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= WAIT_TRIM_DURATION_OFFSET * 0.75f) {
                float bottomStartTrimProgress = (renderProgress - 0.04f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.71f);
                float bottomEndTrimProgress   = ((renderProgress - 0.04f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.71f));

                float bottomEndDistance   = mOriginStartDistance + mWaitPathLength * 0.42f * bottomEndTrimProgress;
                float bottomStartDistance = mOriginStartDistance + mWaitPathLength * 0.42f * bottomStartTrimProgress;
                mWaitPathMeasure.getSegment(bottomStartDistance, bottomEndDistance, mCurrentBottomWaitPath, true);
            }

            //draw the last half : top
            if (renderProgress <= END_TRIM_DURATION_OFFSET && renderProgress > WAIT_TRIM_DURATION_OFFSET) {
                float trimProgress     = ((renderProgress - WAIT_TRIM_DURATION_OFFSET) / (END_TRIM_DURATION_OFFSET - WAIT_TRIM_DURATION_OFFSET));
                float topEndDistance   = mOriginEndDistance + mWaitPathLength * 0.3f + mWaitPathLength * 0.45f * trimProgress;
                float topStartDistance = mOriginStartDistance + mWaitPathLength * 0.48f + mWaitPathLength * 0.27f * trimProgress;
                LogUtils.error(TAG, "topStartDistance = " + topStartDistance + " topEndDistance = " + topEndDistance + "length = " + mWaitPathMeasure.getLength());
                mWaitPathMeasure.getSegment(topStartDistance, topEndDistance, mCurrentTopWaitPath, true);

            }

            //draw the last half : middle
            if (renderProgress > WAIT_TRIM_DURATION_OFFSET + 0.02f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= WAIT_TRIM_DURATION_OFFSET + WAIT_TRIM_DURATION_OFFSET * 0.62f) {
                float middleStartTrimProgress = (renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.02f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.60f);
                float middleEndTrimProgress   = (renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.02f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.60f);

                float middleEndDistance   = mOriginStartDistance + mWaitPathLength * 0.48f + mWaitPathLength * 0.20f * middleEndTrimProgress;
                float middleStartDistance = mOriginStartDistance + mWaitPathLength * 0.48f + mWaitPathLength * 0.10f * middleStartTrimProgress;
                LogUtils.error(TAG, "middleStartDistance = " + middleStartDistance + " middleEndDistance = " + middleEndDistance + "length = " + mWaitPathMeasure.getLength());
                mWaitPathMeasure.getSegment(middleStartDistance, middleEndDistance, mCurrentMiddleWaitPath, true);
            }

            if (renderProgress > WAIT_TRIM_DURATION_OFFSET + 0.62f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= END_TRIM_DURATION_OFFSET) {
                float middleStartTrimProgress = ((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.62f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.38f));
                float middleEndTrimProgress   = ((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.62f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.38f));

                float middleEndDistance   = mOriginStartDistance + mWaitPathLength * 0.68f + mWaitPathLength * 0.325f * middleEndTrimProgress;
                float middleStartDistance = mOriginStartDistance + mWaitPathLength * 0.58f + mWaitPathLength * 0.17f * middleStartTrimProgress;
                LogUtils.error(TAG, "middleStartDistance = " + middleStartDistance + " middleEndDistance = " + middleEndDistance + "length = " + mWaitPathMeasure.getLength());
                mWaitPathMeasure.getSegment(middleStartDistance, middleEndDistance, mCurrentMiddleWaitPath, true);
            }

            //draw the last half : bottom
            if (renderProgress > WAIT_TRIM_DURATION_OFFSET + 0.10f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= WAIT_TRIM_DURATION_OFFSET + WAIT_TRIM_DURATION_OFFSET * 0.70f) {
                float bottomStartTrimProgress = ((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.10f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.60f));
                float bottomEndTrimProgress   = ((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.10f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.60f));

                float bottomEndDistance   = mOriginStartDistance + mWaitPathLength * 0.48f + mWaitPathLength * 0.20f * bottomEndTrimProgress;
                float bottomStartDistance = mOriginStartDistance + mWaitPathLength * 0.48f + mWaitPathLength * 0.10f * bottomStartTrimProgress;
                LogUtils.error(TAG, "bottomStartDistance = " + bottomStartDistance + " bottomEndDistance = " + bottomEndDistance + "length = " + mWaitPathMeasure.getLength());
                mWaitPathMeasure.getSegment(bottomStartDistance, bottomEndDistance, mCurrentBottomWaitPath, true);
            }

            if (renderProgress > WAIT_TRIM_DURATION_OFFSET + 0.70f * WAIT_TRIM_DURATION_OFFSET && renderProgress <= END_TRIM_DURATION_OFFSET) {
                float bottomStartTrimProgress = ((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.70f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.30f));
                float bottomEndTrimProgress   = ((renderProgress - WAIT_TRIM_DURATION_OFFSET - 0.70f * WAIT_TRIM_DURATION_OFFSET) / (WAIT_TRIM_DURATION_OFFSET * 0.30f));

                float bottomEndDistance   = mOriginStartDistance + mWaitPathLength * 0.68f + mWaitPathLength * 0.325f * bottomEndTrimProgress;
                float bottomStartDistance = mOriginStartDistance + mWaitPathLength * 0.58f + mWaitPathLength * 0.17f * bottomStartTrimProgress;
                LogUtils.error(TAG, "bottomStartDistance = " + bottomStartDistance + " bottomEndDistance = " + bottomEndDistance + "length = " + mWaitPathMeasure.getLength());
                mWaitPathMeasure.getSegment(bottomStartDistance, bottomEndDistance, mCurrentBottomWaitPath, true);
            }

            PathMeasure bottomPathMeasure = new PathMeasure(mCurrentBottomWaitPath, false);
            PathMeasure middlePathMeasure = new PathMeasure(mCurrentMiddleWaitPath, false);
            PathMeasure topPathMeasure = new PathMeasure(mCurrentTopWaitPath, false);
            LogUtils.error(TAG, "bottomPathMeasure = " + bottomPathMeasure.getLength() + " middlePathMeasure = " + middlePathMeasure.getLength()
                    + " topPathMeasure = " + topPathMeasure.getLength());
            addDrawTask(drawTask);
        }
    };


}
